package da.metricscraft.client.frames;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GradientPaint;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map.Entry;

import javax.swing.Action;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.StandardChartTheme;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.CategoryLabelPositions;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.labels.StandardPieSectionLabelGenerator;
import org.jfree.chart.labels.StandardPieToolTipGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PiePlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.urls.StandardPieURLGenerator;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.general.Dataset;
import org.jfree.data.general.DefaultPieDataset;
import org.jfree.data.general.PieDataset;
import org.jfree.ui.RectangleInsets;

import da.metricscraft.client.threads.PlayerXMLLoader;
import da.metricscraft.client.threads.PlayerXMLLoader.*;
import da.metricscraft.client.tools.Classifier;
import da.metricscraft.client.tools.Classifier.Classification;

public class StatsPanel extends JPanel implements ActionListener{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private static StatsPanel currentPanel;
	private HashMap<String, ArrayList<PlayerStatsStruct>> data;
	private HashMap<String, Integer> playerLifetime;
	private HashMap<String, Float> percentageUndergroundTime;
	private ArrayList<PlayerStatsStruct> usagePerMinute;
	private ArrayList<PlayerStatsStruct> movementPercentage;
	private ChartPanel frequencyPanel;
	private ChartPanel timePanel;
	private String currentPlayer = "";
	private enum DatasetTypes{
		TIME,
		FREQUENCY,
		CHUNKS
	}
	public static StatsPanel getInstance(){
		if (currentPanel == null){
			currentPanel = new StatsPanel();
		}
		return currentPanel;
	}
	public StatsPanel(){
		super();
		playerLifetime = new HashMap<String, Integer>();
		percentageUndergroundTime = new HashMap<String, Float>();

		ChartFactory.setChartTheme(new StandardChartTheme("JFree/Shadow",
				true));
		setPreferredSize(new Dimension(GraphicsPanel.PANELSIZE[0], GraphicsPanel.PANELSIZE[1]));
		setEnabled(false);
		setLayout(new GridLayout(2,1));

	}
	@Override
	public void setEnabled(boolean enabled){
		super.setEnabled(enabled);
	}
	@Override
	public void repaint(){
		if (data != null){
			getGraphics().clearRect(0, 0, getWidth(), getHeight());
			if (frequencyPanel != null){
				for (Component c : getComponents())
					c.repaint();
						//				timePanel.repaint();
						//				frequencyPanel.repaint();
			}
		}
	}

	public void setPlayer(String selectedItem) {
		// TODO Auto-generated method stub
		System.out.println("Setting player");
		currentPlayer = selectedItem;
		if (data != null)
			buildPanel();
	}
	public void setData(PlayerXMLLoader loader) {
		// TODO Auto-generated method stub
		System.out.println("Setting data");
		data = loader.getData();
		buildPanel();
	}
	private void buildPanel() {
		removeAll();
		usagePerMinute = new ArrayList<PlayerStatsStruct>();
		movementPercentage = new ArrayList<PlayerXMLLoader.PlayerStatsStruct>();
		for(Entry<String, ArrayList<PlayerStatsStruct>> entry : data.entrySet()){
			for(PlayerStatsStruct pss : entry.getValue()){
				if (pss.type == PlayerDataType.LIFETIME)
					playerLifetime.put(entry.getKey(), pss.intValue());
			}
		}
		//--------- TOP PANEL ------------
		JPanel toppanel = new JPanel();
		toppanel.setLayout(new GridLayout(1,2));
		toppanel.setPreferredSize(new Dimension(GraphicsPanel.PANELSIZE[0] - 10, GraphicsPanel.PANELSIZE[1]/2 - 10));

		String player = currentPlayer;
		Dataset dataset = createBarDataset(DatasetTypes.FREQUENCY, player);
		JFreeChart chart = createChart((CategoryDataset)dataset, player);
		frequencyPanel = new ChartPanel(chart);
		frequencyPanel.setFillZoomRectangle(true);
		frequencyPanel.setMouseWheelEnabled(true);
		frequencyPanel.setPreferredSize(new Dimension(GraphicsPanel.PANELSIZE[0]/2 - 10, GraphicsPanel.PANELSIZE[1]/2 - 10));
		toppanel.add(frequencyPanel);

		dataset = createPieDataset(player);
		chart = createPieChart((PieDataset)dataset, player);
		timePanel = new ChartPanel(chart);
		timePanel.setFillZoomRectangle(true);
		timePanel.setMouseWheelEnabled(true);
		timePanel.setPreferredSize(new Dimension(GraphicsPanel.PANELSIZE[0]/2 - 10, GraphicsPanel.PANELSIZE[1]/2 - 10));
		toppanel.add(timePanel);

		toppanel.revalidate();
		add(toppanel);
		//--------------------------------


		//-------BOTTOM PANEL-------------
		JPanel bottomPanel = new JPanel();
		bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.PAGE_AXIS));
		bottomPanel.setPreferredSize(new Dimension(GraphicsPanel.PANELSIZE[0] - 10, GraphicsPanel.PANELSIZE[1]/2 - 10));
		bottomPanel.setBackground(Color.WHITE);
		JLabel temp = new JLabel(player + " spent " + percentageUndergroundTime.get(player) + "% of his time in a mine\n");
		temp.setPreferredSize(new Dimension(100, 20));
		bottomPanel.add(temp);
		if (playerLifetime.get(player) != null && playerLifetime.get(player) < 250000){
			temp = new JLabel("Measures may be inaccurate, player spent insufficient time on the server: " + playerLifetime.get(player));
			bottomPanel.add(temp);
		
		}
		

		JPanel lowerBottomPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));

		lowerBottomPanel.add(addStatsPerMinute("Per minute:", false, 3));
		lowerBottomPanel.add(addStatsPerMinute("Damage taken per minute:", true, 3));
		//classify player
		ArrayList<PlayerStatsStruct> classificationData = new ArrayList<PlayerXMLLoader.PlayerStatsStruct>();
		for (PlayerStatsStruct pss : usagePerMinute){
			classificationData.add(pss);
		}
		for (PlayerStatsStruct pss : movementPercentage){
			classificationData.add(pss);
		}
		if (percentageUndergroundTime.get(player) != null)
			classificationData.add(new PlayerStatsStruct(PlayerDataType.UNDERGROUNDTIME, percentageUndergroundTime.get(player)));

		Classifier.loadData(classificationData);
		HashMap<Classification, Float> deviation = Classifier.calculate();
		//end classify

		//        ------DEVIATION-----
		JPanel suggestionPanel = new JPanel();
		suggestionPanel.setPreferredSize(new Dimension(GraphicsPanel.PANELSIZE[0]/3 - 30, GraphicsPanel.PANELSIZE[1]/2 - 70));
		dataset = createDeviationDataset(deviation);
		chart = createPieChart((PieDataset)dataset, player);
		
		ChartPanel deviationPanel = new ChartPanel(chart);
		deviationPanel.setFillZoomRectangle(true);
		deviationPanel.setMouseWheelEnabled(true);
		deviationPanel.setPreferredSize(new Dimension(GraphicsPanel.PANELSIZE[0]/3 - 30, GraphicsPanel.PANELSIZE[1]/2 - 150));
		JButton improvementSuggestionBtn = new JButton();
		improvementSuggestionBtn.addActionListener(this);
		improvementSuggestionBtn.setActionCommand("suggest");
		improvementSuggestionBtn.setText("Suggest");

		suggestionPanel.add(deviationPanel);
		suggestionPanel.add(improvementSuggestionBtn);
		

		//        --------------------
		lowerBottomPanel.add(suggestionPanel);
		bottomPanel.add(lowerBottomPanel);
		add(bottomPanel);
		//--------------------------------
		revalidate();
	}

	private JPanel addStatsPerMinute(String title, boolean damage, int lowerPanelCount) {
		JPanel dataPanel = new JPanel();
		dataPanel.setLayout(new GridLayout(15,1));
		dataPanel.setPreferredSize(new Dimension(GraphicsPanel.PANELSIZE[0]/lowerPanelCount, GraphicsPanel.PANELSIZE[1]/2 - 10));

		JLabel topicLabel;
		topicLabel = new JLabel(title);
		topicLabel.setPreferredSize(new Dimension(100, 20));
		dataPanel.add(topicLabel);
		Collections.sort(usagePerMinute);


		for (PlayerStatsStruct entry : usagePerMinute){
			boolean containsDamage = entry.type.name().contains("DAMAGE");
			if ((!damage && containsDamage) || (damage && !containsDamage)) continue;

			JPanel container = new JPanel(new FlowLayout(FlowLayout.CENTER));
			container.setBackground(Color.white);

			String text = entry.type.name().toLowerCase();
			JLabel label = new JLabel((damage)?text.replace("damage_taken_",""):text);
			label.setPreferredSize(new Dimension(150, 20));
			container.add(label);

			label = new JLabel(String.format("%f", entry.floatValue()));
			label.setPreferredSize(new Dimension(100, 20));
			container.add(label);

			dataPanel.add(container);
		}

		return dataPanel;
	}

	/**
	 * Creates a chart.
	 *
	 * @param dataset  the dataset.
	 *
	 * @return A chart.
	 */
	private JFreeChart createPieChart(PieDataset dataset, String value) {

		JFreeChart chart = ChartFactory.createPieChart(
				"",  // chart title
				dataset,             // data
				true,                // include legend
				true,
				false
				);
		chart.setBackgroundPaint(new GradientPaint(new Point(0, 0), 
		                new Color(20, 20, 20), new Point(400, 200), Color.DARK_GRAY));
		PiePlot plot = (PiePlot) chart.getPlot();
        plot.setBaseSectionOutlinePaint(Color.WHITE);
        plot.setSectionOutlinesVisible(true);
        plot.setBaseSectionOutlineStroke(new BasicStroke(2.0f));
        plot.setBaseSectionPaint(new GradientPaint(0, 0, new Color(200,200,255), 200, 200, Color.blue));
        
		plot.setSectionOutlinesVisible(true);
		plot.setSimpleLabels(true);
		
		plot.setNoDataMessage("No data available");
		return chart;

	}
	private PieDataset createDeviationDataset(HashMap<Classification, Float> data) {
		DefaultPieDataset dataset = new DefaultPieDataset();
		for (Entry<Classification, Float> entry : data.entrySet()){
			dataset.setValue(entry.getKey().name().toLowerCase().replace("_", " "), entry.getValue());
		}
		return dataset;
	}
	private PieDataset createPieDataset(String playername) {
		DefaultPieDataset dataset = new DefaultPieDataset();

		ArrayList<PlayerDataType> temp = new ArrayList<PlayerXMLLoader.PlayerDataType>();

		for(Entry<String, ArrayList<PlayerStatsStruct>> entry : data.entrySet()){
			if (!entry.getKey().equals(playername)) continue;

			ArrayList<PlayerStatsStruct> values = entry.getValue(); 
			Collections.sort(values);

			float percentage = 0;
			int totalMoveTime = 0;
			int undergroundTime = 0;

			PlayerDataType[] types = PlayerDataType.values();
			ArrayList<String> typeslist = new ArrayList<String>();
			for (int i = 0; i < types.length; i++){
				typeslist.add(types[i].name());
			}
			Collections.sort(typeslist);

			int statsEntry = 0;
			for(String currentType : typeslist){


				try{
					PlayerStatsStruct pss = values.get(statsEntry);

					if (!pss.type.name().equals(currentType)) throw new Exception("Info: Entry not found!");

					if (pss.type.name().contains("TIME")){
						if (pss.type != PlayerDataType.LIFETIME && pss.type != PlayerDataType.UNDERGROUNDTIME){
							if (pss.type == PlayerDataType.RUNTIME || pss.type == PlayerDataType.SPRINTTIME || pss.type == PlayerDataType.CROUCHTIME || pss.type == PlayerDataType.SWIMTIME || pss.type == PlayerDataType.BOATTIME)
								totalMoveTime += pss.floatValue();
							float value =  pss.floatValue();

							percentage += value;
							dataset.setValue(pss.type.name().replace("TIME", "").toLowerCase(), value);
							movementPercentage.add(new PlayerStatsStruct(pss.type, pss.floatValue()));
							temp.add(pss.type);
							statsEntry++;
						}else if (pss.type == PlayerDataType.UNDERGROUNDTIME){
							undergroundTime = pss.intValue();
							statsEntry++;
						}else
							statsEntry++;
					}else{
						statsEntry++;
						throw new Exception("Info: No Time value");
					}
				}catch (Exception e){
					if (currentType.contains("TIME") && !currentType.contains("UNDERGROUND") && !currentType.contains("LIFE")){

						movementPercentage.add(new PlayerStatsStruct(PlayerDataType.valueOf(currentType), 0));
						dataset.setValue(currentType.replace("TIME", "").toLowerCase(), 0.01);
					}
				}
			}

			this.percentageUndergroundTime.put(entry.getKey(), (float)undergroundTime/totalMoveTime);

			dataset.setValue("idle",playerLifetime.get(playername) - percentage);
			for (PlayerStatsStruct pss : movementPercentage){
				pss.setValue(pss.floatValue()/percentage);
			}
		}


		return dataset;
	}
	/**
	 * Returns a sample dataset.
	 *
	 * @return The dataset.
	 */
	private CategoryDataset createBarDataset(DatasetTypes type, String player) {

		DefaultCategoryDataset dataset = new DefaultCategoryDataset();

		for(Entry<String, ArrayList<PlayerStatsStruct>> entry : data.entrySet()){
			if (!player.equals(entry.getKey())) continue;

			ArrayList<PlayerStatsStruct> values = entry.getValue(); 

			Collections.sort(values);

			PlayerDataType[] types = PlayerDataType.values();
			ArrayList<String> typeslist = new ArrayList<String>();
			for (int i = 0; i < types.length; i++){
				typeslist.add(types[i].name());
			}
			Collections.sort(typeslist);

			int statsEntry = 0;
			for (String currentType : typeslist){

				try{
					PlayerStatsStruct pss = values.get(statsEntry);
					if (!pss.type.name().equals(currentType)) throw new Exception("Info: Entry not found!");
					if (!pss.type.name().contains("TIME")){
						if (!pss.type.name().contains("DAMAGE") && !pss.type.name().contains("CHUNKS"))
							dataset.addValue(pss.floatValue(), pss.type, "");
						usagePerMinute.add(new PlayerStatsStruct(pss.type, pss.floatValue() * 20*60/playerLifetime.get(player)));
						statsEntry++;
					}else{
						statsEntry++;
						throw new Exception("Info: Chunks or Time or Damage");
					}
				}catch (Exception e){
					if (!currentType.contains("CHUNKS") && !currentType.contains("TIME") && !currentType.contains("DAMAGE"))
						dataset.addValue(0, currentType, "");
				}
			}
		}



		return dataset;

	}

	/**
	 * Creates a sample chart.
	 *
	 * @param dataset  the dataset.
	 *
	 * @return The chart.
	 */
	private JFreeChart createChart(CategoryDataset dataset, String value) {

		// create the chart...
		JFreeChart chart = ChartFactory.createBarChart(
				value,       // chart title
				"",               // domain axis label
				"Amount",                  // range axis label
				dataset,                  // data
				PlotOrientation.VERTICAL, // orientation
				true,                     // include legend
				true,                     // tooltips?
				false                     // URLs?
				);

		// NOW DO SOME OPTIONAL CUSTOMISATION OF THE CHART...

		// set the background color for the chart...
		//chart.setBackgroundPaint(Color.white);

		// get a reference to the plot for further customisation...
		CategoryPlot plot = (CategoryPlot) chart.getPlot();

		// ******************************************************************
		//  More than 150 demo applications are included with the JFreeChart
		//  Developer Guide...for more information, see:
		//
		//  >   http://www.object-refinery.com/jfreechart/guide.html
		//
		// ******************************************************************

		// set the range axis to display integers only...
		NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
		rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());

		plot.setOutlinePaint(Color.WHITE);
        plot.setOutlineStroke(new BasicStroke(2.0f));
        plot.setOutlineVisible(true);
        
		// disable bar outlines...
		BarRenderer renderer = (BarRenderer) plot.getRenderer();
		//renderer.setDrawBarOutline(false);
		// set up gradient paints for series...
		GradientPaint gp0 = new GradientPaint(0.0f, 0.0f, Color.blue,
				1.0f, 1.0f, new Color(100, 100, 255));
		GradientPaint gp1 = new GradientPaint(0.0f, 0.0f, Color.green,
				0.0f, 0.0f, new Color(0, 64, 0));
		GradientPaint gp2 = new GradientPaint(0.0f, 0.0f, Color.red,
				0.0f, 0.0f, new Color(64, 0, 0));
		renderer.setSeriesPaint(0, gp0);
		renderer.setSeriesPaint(1, gp1);
		renderer.setSeriesPaint(2, gp2);

		CategoryAxis domainAxis = plot.getDomainAxis();
		domainAxis.setCategoryLabelPositions(
				CategoryLabelPositions.createUpRotationLabelPositions(
						Math.PI / 6.0));
		// OPTIONAL CUSTOMISATION COMPLETED.

		return chart;

	}
	@Override
	public void actionPerformed(ActionEvent event) {
		// TODO Auto-generated method stub
		if (event.getActionCommand() == "suggest"){
			ArrayList<Classification> adaptLevel = new ArrayList<Classifier.Classification>();
			for (Entry<Classification, Float> entry : Classifier.getValuesOver(0.6f).entrySet()){
				int action = JOptionPane.showConfirmDialog(this, currentPlayer + " acts like a " + 
							entry.getKey().name().toLowerCase().replace("_", "") + ".\n\n\nDo you want to adapt the game?\n\n\n");
				//0 = yes, 1 = no, 2 = cancel
				if (action == 0){
					adaptLevel.add(entry.getKey());
				}
			}
			
		}
	}

}
